<!--
$RCSfile: package.html,v $


Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met: 

- Redistribution of source code must retain the above copyright 
  notice, this  list of conditions and the following disclaimer.

- Redistribution in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in 
  the documentation and/or other materials provided with the
  distribution.

Neither the name of Sun Microsystems, Inc. or the names of 
contributors may be used to endorse or promote products derived 
from this software without specific prior written permission.

This software is provided "AS IS," without a warranty of any 
kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES. 

You acknowledge that this software is not designed or intended for 
use in the design, construction, operation or maintenance of any 
nuclear facility. 

$Revision: 1.12 $
$Date: 2006/05/01 22:55:50 $
$State: Exp $
-->

<html>
<body>
Package containing the public classes used by the TIFF plug-in for
the Image I/O Framework.

<p>
<a href="#Reading">Reading Images</a><br/>
<font size="-1">
<ul>
<li><a href="#Decompression">Decompression</a></li>
<li><a href="#ColorConversionRead">Color Conversion</a></li>
<li><a href="#ColorSpacesRead">Color Spaces</a></li>
<li><a href="#ICCProfilesRead">ICC Profiles</a></li>
<li><a href="#MetadataIssuesRead">Metadata Issues</a>
<font size="-2">
<ul>
<li><a href="#MapNativeStandard">Native to Standard Metadata Mapping</a></li>
</ul>
</font>
</li>
<li><a href="#EXIFRead">Reading EXIF Images</a>
<font size="-2">
<ul>
<li><a href="#EXIFReadTIFF">Reading Uncompressed EXIF Images</a></li>
<li><a href="#EXIFReadJPEG">Reading Compressed EXIF Images</a></li>
</ul>
</font>
</li>
</ul>
</font>
<a href="#Writing">Writing Images</a><br/>
<font size="-1">
<ul>
<li><a href="#Compression">Compression</a></li>
<li><a href="#ColorConversionWrite">Color Conversion</a></li>
<li><a href="#ICCProfilesWrite">ICC Profiles</a></li>
<li><a href="#MetadataIssuesWrite">Metadata Issues</a></li>
<font size="-2">
<ul>
<li><a href="#MapStandardNative">Standard to Native Metadata Mapping</a></li>
</ul>
</font>
<li><a href="#EXIFWrite">Writing EXIF Images</a>
<font size="-2">
<ul>
<li><a href="#EXIFWriteTIFF">Writing Uncompressed EXIF Images</a></li>
<li><a href="#EXIFWriteJPEG">Writing Compressed EXIF Images</a></li>
</ul>
</font>
</li>
</ul>
</font>
<a href="#StreamMetadata">Native Stream Metadata Format</a><br/>
<a href="#ImageMetadata">Native Image Metadata Format</a>
</p>

<h3><a name="Reading"/>Reading Images</h3>

TIFF images are read by an {@link javax.imageio.ImageReader} which may be
controlled by its public interface as well as via a supplied
{@link com.github.jaiimageio.plugins.tiff.TIFFImageReadParam}.

<!-- <h4>Supported Image Types</h4> -->

<!-- Table? -->

<h4><a name="Decompression"/>Decompression</h4>

A {@link com.github.jaiimageio.plugins.tiff.TIFFDecompressor} object may be
supplied via
{@link com.github.jaiimageio.plugins.tiff.TIFFImageReadParam#setTIFFDecompressor
TIFFImageReadParam.setTIFFDecompressor()}.
If a {@link com.github.jaiimageio.plugins.tiff.TIFFDecompressor} is specified
in this manner it will be used and will supersede any internal decompressor
which might otherwise have been used for a known compression type. This
mechanism allows for compression types to be handled by user-defined
decompressors whether or not that compression type is known to the plug-in.

<h4><a name="ColorConversionRead"/>Color Conversion</h4>

A {@link com.github.jaiimageio.plugins.tiff.TIFFColorConverter} object may be
supplied via
{@link com.github.jaiimageio.plugins.tiff.TIFFImageReadParam#setColorConverter
TIFFImageReadParam.setColorConverter()}.
If a {@link com.github.jaiimageio.plugins.tiff.TIFFColorConverter} is specified
in this manner it will be used and will supersede any internal color converter
which might otherwise have been used. This color converter will be used to
convert the source image data to an RGB color space.

<p>If no user-supplied color converter is available, the source image data
have photometric type CIE L*a*b* or YCbCr, and the destination color space
type is RGB, then the source image data will be automatically converted to
RGB using an internal color converter.</p>

<h4><a name="ColorSpacesRead"/>Color Spaces</h4>

The raw color space assigned by default, i.e., in the absence of a
user-supplied {@link javax.imageio.ImageTypeSpecifier}, will be
the first among the following which applies:

<ul>
<li>A color space created from the <a href="#ICCProfilesRead">ICC Profile</a>
metadata field if it is present and compatible with the image data
layout.</li>
<a name="nonICCProfile"><li>sRGB if the image is monochrome/bilevel
(a two-level color map is created internally).</li>
<li>sRGB if the image is palette-color.</li>
<li>Linear RGB if the image has three samples per pixel, has photometric type
CIE L*a*b*, or has photometric type YCbCr and is <i>not</i>
JPEG-compressed.</li>
<li>A <a href="#DefaultCMYK">default CMYK color space</a> if the image has
photometric type CMYK and four samples per pixel.</li>
<li>Grayscale if the image has one or two samples per pixel and uniformly
1, 2, 4, 8, 16, or 32 bits per sample or is floating point.</li>
<li>sRGB if the image has three or four samples per pixel and uniformly
1, 2, 4, 8, 16, or 32 bits per sample or is floating point.</li>
<li>A fabricated, <a href="#GenericCS">generic color space</a> if the image
has more than four samples per pixel and the number of bits per sample for
all bands is the same and is a multiple of 8.</li>
<li>Grayscale if the image has one or two samples per pixel regardless of
the number of bits per sample.</li>
<li>sRGB if the image has three or four samples per pixel regardless of
the number of bits per sample.</li>
<li>A fabricated, <a href="#GenericCS">generic color space</a> if the image
has more than four samples per pixel regardless of the number of bits per
sample.</li>
</ul>

<p><a name="DefaultCMYK"/>The normalized color coordinate transformations
used for the default CMYK color space are defined as follows:

<ul>
<li>CMYK to linear RGB
<pre>
R = (1 - K)*(1 - C)
G = (1 - K)*(1 - M)
B = (1 - K)*(1 - Y)
</pre>
</li>
<li>Linear RGB to CMYK
<pre>
K = min{1 - R, 1 - G, 1 - B}
if(K != 1) {
    C = (1 - R - K)/(1 - K)
    M = (1 - G - K)/(1 - K)
    Y = (1 - B - K)/(1 - K)
} else {
    C = M = Y = 0
}
</pre>
</li>
</ul>
</p>

<p><a name="GenericCS"/>The generic color space used when no other color space
can be inferred is provided merely to enable the data to be loaded. It is not
intended to provide accurate conversions of any kind.</p>

<p>If the data are known to be in a color space not correctly handled by the
foregoing, then an {@link javax.imageio.ImageTypeSpecifier} should be supplied
to the reader and should be derived from a color space which is correct for
the data in question.</p>

<h4><a name="ICCProfilesRead"/>ICC Profiles</h4>

If an ICC profile is contained in the image metadata
({@link com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet#TAG_ICC_PROFILE},
tag number 34675), an attempt will be made to use it to create the color space
of the loaded image. It will be used if the data layout is of component type
and the number of samples per pixel equals or is one greater than the number
of components described by the ICC profile. If the ICC profile is not used
then the color space will be inferred in one of the subsequent steps described
<a href="#nonICCProfile">above</a>.

<p>If for some reason the embedded ICC profile is not used automatically, then
it may be used manually by following this procedure:

<ol>
<li>Obtain the image metadata from
{@link javax.imageio.ImageReader#getImageMetadata}</li>
<li>Extract the ICC profile field and its value.</li>
<li>Create an {@link java.awt.color.ICC_ColorSpace} from an
{@link java.awt.color.ICC_Profile} created from the ICC profile field data
using {@link java.awt.color.ICC_Profile#getInstance(byte[])}.</li>
<li>Create an {@link javax.imageio.ImageTypeSpecifier} from the new color
space using one of its factory methods which accepts an
{@link java.awt.color.ICC_ColorSpace}.
<li>Create a compatible {@link javax.imageio.ImageReadParam} and set the
{@link javax.imageio.ImageTypeSpecifier} using
{@link javax.imageio.ImageReadParam#setDestinationType}.</li>
<li>Pass the parameter object to the appropriate <code>read</code> method.</li>
</ol>

<p>If the inferred color space not based on the ICC Profile field is compatible
with the ICC profile-based color space, then a second
{@link javax.imageio.ImageTypeSpecifier} derived from this inferred color
space will be included in the {@link java.util.Iterator} returned by
{@link javax.imageio.ImageReader#getImageTypes}. If the iterator contains
more than one type, the first one will be based on the ICC profile and the
second on the inferred color space.</p>

<h4><a name="MetadataIssuesRead"/>Metadata Issues</h4>

By default all fields in the TIFF image file directory (IFD) are loaded into
the native image metadata object. In cases where the IFD includes fields which
contain large amounts of data this could be very inefficient. Which fields
are loaded may be controlled by setting which TIFF tags the reader is allowed
to recognize and whether it is ignoring metadata. The reader is informed to
disregard metadata as usual via the <code>ignoreMetadata</code> parameter of
{@link javax.imageio.ImageReader#setInput(Object,boolean,boolean)
javax.imageio.ImageReader.setInput()}. It is
informed of which {@link com.github.jaiimageio.plugins.tiff.TIFFTag}s to
recognize or not to recognize via
{@link com.github.jaiimageio.plugins.tiff.TIFFImageReadParam#addAllowedTagSet(TIFFTagSet)
TIFFImageReadParam.addAllowedTagSet()}
and
{@link com.github.jaiimageio.plugins.tiff.TIFFImageReadParam#removeAllowedTagSet(TIFFTagSet)
TIFFImageReadParam.removeAllowedTagSet()}.
If <code>ignoreMetadata</code> is <code>true</code>, then the reader will
load into the native image metadata object only those fields which have a
<code>TIFFTag</code> contained in the one of the allowed
<code>TIFFTagSet</code>s.

<p>Use of a {@link com.github.jaiimageio.plugins.tiff.TIFFDirectory} object
may simplify gaining access to metadata values. An instance of
<code>TIFFDirectory</code> may be created from the <code>IIOMetadata</code>
object returned by the TIFF reader using the
{@link com.github.jaiimageio.plugins.tiff.TIFFDirectory#createFromMetadata
TIFFDirectory.createFromMetadata()} method.</p>

<h5><a name="MapNativeStandard"/>
Mapping of TIFF Native Image Metadata to the Standard Metadata Format</h5>

The derivation of standard metadata format <code>javax_imageio_1.0</code>
elements from <a href="#ImageMetadata">TIFF native image metadata</a> is given
in the following table.

<p>
<table border="1">
<tr>
<th>Standard Metadata Element</th>
<th>Derivation from TIFF Fields</th>
</tr>
<tr>
<td>/Chroma/ColorSpaceType@name</td>
<td>PhotometricInterpretation: WhiteIsZero, BlackIsZero, TransparencyMask =
"GRAY"; RGB, PaletteColor => "RGB"; CMYK => "CMYK";
YCbCr => "YCbCr";
CIELab, ICCLab => "Lab".</td>
</tr>
<tr>
<td>/Chroma/NumChannels@value</td>
<td>SamplesPerPixel</td>
</tr>
<tr>
<td>/Chroma/BlackIsZero@value</td>
<td>"TRUE" <=> PhotometricInterpretation => WhiteIsZero</td>
</tr>
<tr>
<td>/Chroma/Palette</td>
<td>ColorMap</td>
</tr>
<tr>
<td>/Compression/CompressionTypeName@value</td>
<td>Compression: Uncompressed => "none"; CCITT 1D => "CCITT
RLE";
Group 3 Fax => "CCITT T.4"; Group 4 Fax => "CCITT T.6";
LZW => "LZW";
JPEG => "Old JPEG"; New JPEG => "JPEG"; Zlib =>> "ZLib"; PackBits =>
"PackBits";
Deflate => "Deflate"; EXIF JPEG => "JPEG".</td>
</tr>
<tr>
<td>/Compression/Lossless@value</td>
<td>Compression: JPEG or New JPEG => "FALSE"; otherwise "TRUE".</td>
</tr>
<tr>
<td>/Data/PlanarConfiguration@value</td>
<td>Chunky => "PixelInterleaved"; Planar => "PlaneInterleaved".</td>
</tr>
<tr>
<td>/Data/SampleFormat@value</td>
<td>PhotometricInterpretation PaletteColor => "Index";
SampleFormat unsigned integer data => "UnsignedIntegral";
SampleFormat two's complement signed integer data => "SignedIntegral";
SampleFormat IEEE floating point data => "Real";
otherwise element not emitted.
</td>
</tr>
<tr>
<td>/Data/BitsPerSample@value</td>
<td>BitsPerSample as a space-separated list.</td>
</tr>
<tr>
<td>/Data/SampleMSB@value</td>
<td>FillOrder: left-to-right => space-separated list of BitsPerSample-1;
right-to-left => space-separated list of 0s.</td>
</tr>
<tr>
<td>/Dimension/PixelAspectRatio@value</td>
<td>(1/XResolution)/(1/YResolution)</td>
</tr>
<tr>
<td>/Dimension/ImageOrientation@value</td>
<td>Orientation</td>
</tr>
<tr>
<td>/Dimension/HorizontalPixelSize@value</td>
<td>1/XResolution in millimeters if ResolutionUnit is not None.</td>
</tr>
<tr>
<td>/Dimension/VerticalPixelSize@value</td>
<td>1/YResolution in millimeters if ResolutionUnit is not None.</td>
</tr>
<tr>
<td>/Dimension/HorizontalPosition@value</td>
<td>XPosition in millimeters if ResolutionUnit is not None.</td>
</tr>
<tr>
<td>/Dimension/VerticalPosition@value</td>
<td>YPosition in millimeters if ResolutionUnit is not None.</td>
</tr>
<tr>
<td>/Document/FormatVersion@value</td>
<td>6.0</td>
</tr>
<tr>
<td>/Document/SubimageInterpretation@value</td>
<td>NewSubFileType: transparency => "TransparencyMask";
reduced-resolution => "ReducedResolution";
single page => "SinglePage".</td>
</tr>
<tr>
<td>/Document/ImageCreationTime@value</td>
<td>DateTime</td>
</tr>
<tr>
<td>/Text/TextEntry</td>
<td>DocumentName, ImageDescription, Make, Model, PageName, Software,
Artist, HostComputer, InkNames, Copyright:
/Text/TextEntry@keyword = field name,
/Text/TextEntry@value = field value.<br>
Example: TIFF Software field => /Text/TextEntry@keyword = "Software",
/Text/TextEntry@value = Name and version number of the software package(s)
used to create the image.</td>
</tr>
<tr>
<td>/Transparency/Alpha@value</td>
<td>ExtraSamples: associated alpha => "premultiplied";
unassociated alpha => "nonpremultiplied".</td>
</tr>
</table>
</p>

<h4><a name="EXIFRead"/>Reading EXIF Images</h4>

The TIFF reader may be used to read an uncompressed EXIF image or the
contents of the <tt>APP1</tt> marker segment of a compressed EXIF image.

<h5><a name="EXIFReadTIFF"/>Reading Uncompressed EXIF Images</h5>

An uncompressed EXIF image is a one- or two-page uncompressed TIFF image
with a specific ordering of its IFD and image data content. Each pixel
has three 8-bit samples with photometric interpretation RGB or YCbCr.
The image stream must contain a single primary image and may contain a
single thumbnail which if present must also be uncompressed. The usual
{@link javax.imageio.ImageReader} methods may be used to read the image
data and metadata:

<pre><code>
ImageReader tiffReader;
ImageInputStream input;
ImageReadParam readParam;

tiffReader.setInput(input);

// Read primary image and IFD.
BufferedImage image = tiffReader.read(0, readParam);
IIOMetadata primaryIFD = tiffReader.getImageMetadata(0);

// Read thumbnail and IFD if present.
BufferedImage thumbnail = null;
IIOMetadata thumbnailIFD = null;
if(tiffReader.getNumImages(true) > 1) {
    thumbnail = tiffReader.read(1, readParam);
    thumbnailIFD = tiffReader.getImageMetadata(1);
}
</code></pre>

Note that the EXIF thumbnail is treated as a separate page in the TIFF
stream and not as a thumbnail, i.e.,
<code>tiffReader.hasThumbnails(0)</code> will return <code>false</code>.

<h5><a name="EXIFReadJPEG"/>Reading Compressed EXIF Images</h5>

A compressed EXIF image is a 3-band ISO/IEC 10918-1 baseline DCT JPEG stream
with an inserted <tt>APP1</tt> marker segment. The parameters of the marker
segment after the length are the 6-byte sequence
<code>{'E',&nbsp;'x',&nbsp;'i',&nbsp;'f',&nbsp;0x00,&nbsp;0x00}</code> followed
by a complete TIFF stream. The embedded TIFF stream contains a primary IFD
describing the JPEG image optionally followed by a thumbnail IFD and
compressed or uncompressed thumbnail image data. Note that the embedded TIFF
stream does not contain any image data associated with the primary IFD
nor any descriptive fields which duplicate information found in the JPEG
stream itself.

<p>The parameter content of the <tt>APP1</tt> marker segment may be obtained
from the user object of the associated <code>Node</code> in a
<tt>javax_imageio_jpeg_image_1.0</tt> native image metadata tree extracted
from the image metadata object returned by the JPEG reader. This node will
have name <tt>unknown</tt> and an attribute named <tt>MarkerTag</tt> with
integral value <code>0xE1</code> (<code>String</code> value
<code>"225"</code>). The primary IFD and the thumbnail IFD and image may be
read from the user object by the usual {@link javax.imageio.ImageReader}
methods:

<pre><code>
ImageReader tiffReader;
ImageReadParam readParam;
IIOMetadataNode app1EXIFNode;

// Set up input skipping EXIF ID 6-byte sequence.
byte[] app1Params = (byte[])app1EXIFNode.getUserObject();
MemoryCacheImageInputStream app1EXIFInput =
    new MemoryCacheImageInputStream(new ByteArrayInputStream(app1Params, 6,
                                                             app1Params.length - 6));
tiffReader.setInput(app1EXIFInput);

// Read primary IFD.
IIOMetadata primaryIFD = tiffReader.getImageMetadata(0);

// Read thumbnail and IFD if present.
BufferedImage thumbnail = null;
IIOMetadata thumbnailIFD = null;
if(tiffReader.getNumImages(true) > 1) {
    thumbnail = tiffReader.read(1, readParam);
    thumbnailIFD = tiffReader.getImageMetadata(1);
}
</core></pre>
Note that <code>tiffReader.getNumImages(true)</code> returns the number of
IFDs in the embedded TIFF stream including those corresponding to empty
images. Calling <code>tiffReader.read(0,&nbsp;readParam)</code> will throw
an exception as the primary image in the embedded TIFF stream is always
empty; the primary image should be obtained using the JPEG reader itself.
</p>

<h3><a name="Writing"/>Writing Images</h3>

TIFF images are written by an {@link javax.imageio.ImageWriter} which may be
controlled by its public interface as well as via a supplied
{@link com.github.jaiimageio.plugins.tiff.TIFFImageWriteParam}. The TIFF
writer supports many optional capabilities including writing tiled images,
inserting images, writing or inserting empty images, and replacing image
data. Pixels may be replaced in either empty or non-empty images but if and
only if the data are not compressed.

<!-- <h4>Supported Image Types</h4> -->

<!-- Table? -->

<h4><a name="Compression"/>Compression</h4>

A {@link com.github.jaiimageio.plugins.tiff.TIFFCompressor} object may be
supplied via
{@link com.github.jaiimageio.plugins.tiff.TIFFImageWriteParam#setTIFFCompressor
TIFFImageWriteParam.setTIFFCompressor()}.
If a {@link com.github.jaiimageio.plugins.tiff.TIFFCompressor} is specified
in this manner it will be used and will supersede any internal compressor
which might otherwise have been used for a known compression type. This
mechanism allows for compression types to be handled by user-defined
compressors whether or not that compression type is known to the plug-in.

<h4><a name="ColorConversionWrite"/>Color Conversion</h4>

A {@link com.github.jaiimageio.plugins.tiff.TIFFColorConverter} object may be
supplied via
{@link com.github.jaiimageio.plugins.tiff.TIFFImageWriteParam#setColorConverter
TIFFImageWriteParam.setColorConverter()}.
If a {@link com.github.jaiimageio.plugins.tiff.TIFFColorConverter} is specified
in this manner it will be used and will supersede any internal color converter
which might otherwise have been used. This color converter will be used to
convert from RGB to the color space of the output image.

<p>If no user-supplied color converter is available, the source image data
color space type is RGB, and the destination photometric type is CIE L*a*b* or
YCbCr, then the source image data will be automatically converted from
RGB using an internal color converter.</p>

<h4><a name="ICCProfilesWrite"/>ICC Profiles</h4>

An <tt>ICC Profile</tt> field will be written if either:
<ul>
<li>one is present in the native image metadata
{@link javax.imageio.metadata.IIOMetadata} instance supplied to the writer,
or</li>
<li>the {@link java.awt.color.ColorSpace} of the destination
<code>ImageTypeSpecifier</code> is an instance of
{@link java.awt.color.ICC_ColorSpace} which is not one of the standard
color spaces defined by the <tt>CS_*</tt> constants in the
<code>ColorSpace</code> class. The destination type is set via
{@link javax.imageio.ImageWriteParam#setDestinationType(ImageTypeSpecifier)
ImageWriteParam.setDestinationType()} and defaults to the
<code>ImageTypeSpecifier</code> of the image being written.
</li>
</ul>

<h4><a name="MetadataIssuesWrite"/>Metadata Issues</h4>

Some behavior of the writer is affected by or may affect the contents of
the image metadata which may be supplied by the user.

<p>For bilevel images, the <tt>FillOrder</tt>, and <tt>T4Options</tt>
fields affect the output data. The data will be filled right-to-left if
<tt>FillOrder</tt> is present with a value of 2
({@link com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet#FILL_ORDER_RIGHT_TO_LEFT})
and will be filled left-to-right otherwise. The value of <tt>T4Options</tt>
specifies whether the data should be 1D- or 2D-encoded and whether EOL
padding should be used.</p>

<p>For all images the value of the <tt>RowsPerStrip</tt> field is used
to the set the number of rows per strip if the image is not tiled. The
default number of rows per strip is either 8 or the number of rows which
would fill no more than 8 kilobytes, whichever is larger.</p>

<p>For all images the tile dimensions may be set using the <tt>TileWidth</tt>
and <tt>TileLength</tt> field values if the tiling mode is
{@link javax.imageio.ImageWriteParam#MODE_COPY_FROM_METADATA}. If this mode
is set but the fields are not, their respective default values are the image
width and height.</p>

<p>When using JPEG-in-TIFF compression, a <tt>JPEGTables</tt> field will be
written to the IFD and abbreviated JPEG streams to each strip or tile if and
only if a <tt>JPEGTables</tt> field is contained in the metadata object
provided to the writer. If the contents of the <tt>JPEGTables</tt> field is
a valid tables-only JPEG stream, then it will be used; otherwise the contents
of the field will be replaced with default visually lossless tables. If no
such <tt>JPEGTables</tt> field is present in the metadata, then no
<tt>JPEGTables</tt> field will be written to the output and each strip or
tile will be written as a separate, self-contained JPEG stream.</p>

<p>When using Deflate/ZLib or LZW compression, if the image has 8 bits per
sample, a horizontal differencing predictor will be used if the
<tt>Predictor</tt> field is present with a value of 2
({@link com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet#PREDICTOR_HORIZONTAL_DIFFERENCING}). If prediction is so requested but the image does not have
8 bits per sample the field will be reset to have the value 1
({@link com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet#PREDICTOR_NONE}).
</p>

<p>Some fields may be added or modified:

<ul>
<li><tt>PhotometricInterpretation</tt> if not present or if derived
from a <code>TIFFColorConverter</code>.</li>
<li><tt>PlanarConfiguration</tt> if this field is present with value
<tt>Planar</tt> is is reset to <tt>Chunky</tt>.</li>
<li><tt>Compression</tt> always.</li>
<li><tt>BitsPerSample</tt> if the image is not bilevel.</li>
<li><tt>SamplesPerPixel</tt> always.</li>
<li><tt>ExtraSamples</tt> if an alpha channel is present.</li>
<li><tt>SampleFormat</tt> if not present and the data are 16- or 32-bit
integers or floating point.</li>
<li><tt>ColorMap</tt> if the <tt>PhotometricInterpretation</tt> is
<tt>RGBPalette</tt>.</li>
<li><tt>ImageWidth</tt> and <tt>ImageLength</tt> always.</li>
<li><tt>TileWidth</tt>, <tt>TileLength</tt>, <tt>TileOffsets</tt>, and
<tt>TileByteCounts</tt> if a tiled image is being written.</li>
<li><tt>RowsPerStrip</tt>, <tt>StripOffsets</tt>, and <tt>StripByteCounts</tt>
if a tiled image is <i>not</i> being written.</li>
<li><tt>XResolution</tt>, <tt>YResolution</tt>, and <tt>ResolutionUnit</tt>
if none of these is present.</li>
<li><tt>YCbCrSubsampling</tt> and <tt>YCbCrPositioning</tt> if the
photometric interpretation is YCbCr and the compression type is not JPEG
(only [1,&nbsp;1] subsampling and cosited positioning are supported for
non-JPEG YCbCr output).</li>
<li><tt>YCbCrSubsampling</tt>, <tt>YCbCrPositioning</tt>, and
<tt>ReferenceBlackWhite</tt>: if the compression type is JPEG and the color
space is RGB these will be reset to [2,&nbsp;2] centered subsampling with no
headroom/footroom (0:255,128:255,128:255).</li>
</ul>

<p>Some fields may be removed:

<ul>
<li><tt>BitsPerSample</tt> if the image is bilevel.</li>
<li><tt>ExtraSamples</tt> if the image does not have an alpha channel.</li>
<li><tt>ColorMap</tt> if the photometric interpretation is not
<tt>RGBPalette</tt>.</li>
<li><tt>TileWidth</tt>, <tt>TileLength</tt>, <tt>TileOffsets</tt>, and
<tt>TileByteCounts</tt> if tiling <i>is not</i> being used.</li>
<li><tt>RowsPerStrip</tt>, <tt>StripOffsets</tt>, and <tt>StripByteCounts</tt>
if tiling <i>is</i> being used.</li>
<li><tt>YCbCrSubsampling</tt>, <tt>YCbCrPositioning</tt>, and
<tt>ReferenceBlackWhite</tt> if the compression type is JPEG and the
color space is grayscale.</li>
</ul>
</p>

<p>Other fields present in the supplied metadata are uninterpreted and will
be written as supplied.</p>

<p>If an EXIF image is being written, the set of fields present and their
values will be modified such that the result is in accord with the EXIF 2.2
specification.</p>

<p>Setting up the image metadata to write to a TIFF stream may be simplified
by using the {@link com.github.jaiimageio.plugins.tiff.TIFFDirectory} class
which represents a TIFF IFD. A field in a TIFF IFD is represented by an
instance of {@link com.github.jaiimageio.plugins.tiff.TIFFField}. For each
field to be written a <code>TIFFField</code> may be added to the
<code>TIFFDirectory</code> and the latter converted to an
<code>IIOMetadata</code> object by invoking
{@link com.github.jaiimageio.plugins.tiff.TIFFDirectory#getAsMetadata}. The
<code>IIOMetadata</code> object so obtained may then be passed to the TIFF
writer.</p>

<h5><a name="MapStandardNative"/>
Mapping of the Standard Metadata Format to TIFF Native Image Metadata</h5>

The derivation of <a href="#ImageMetadata">TIFF native image metadata</a>
elements from the standard metadata format <code>javax_imageio_1.0</code> is
given in the following table.

<p>
<table border="1">
<tr>
<th>TIFF Field</th>
<th>Derivation from Standard Metadata Elements</th>
</tr>
<tr>
<td>
PhotometricInterpretation
</td>
<td>/Chroma/ColorSpaceType@name: "GRAY" and /Chroma/BlackIsZero@value = "FALSE"
=> WhiteIsZero; "GRAY" and /Document/SubimageInterpretation@value =
"TransparencyMask" => TransparencyMask; "RGB" and /Chroma/Palette present =>
PaletteColor; "GRAY" => BlackIsZero; "RGB" => RGB; "YCbCr" => YCbCr;
"CMYK" => CMYK; "Lab" => CIELab.</td>
</tr>
<tr>
<td>SamplesPerPixel</td>
<td>/Chroma/NumChannels@value</td>
</tr>
<tr>
<td>ColorMap</td>
<td>/Chroma/Palette</td>
</tr>
<tr>
<td>Compression</td>
<td>/Compression/CompressionTypeName@value: "none" => Uncompressed;
"CCITT RLE" => CCITT 1D; "CCITT T.4" => Group 3 Fax; "CCITT T.6" => Group 4
Fax; "LZW" => LZW; "Old JPEG" => JPEG; "JPEG" => New JPEG; "ZLib" => ZLib;
"PackBits" => PackBits; "Deflate" => Deflate.</td>
</tr>
<tr>
<td>PlanarConfiguration</td>
<td>/Data/PlanarConfiguration@value: "PixelInterleaved" => Chunky;
"PlaneInterleaved" => Planar.</td>
</tr>
<tr>
<td>SampleFormat</td>
<td>/Data/SampleFormat@value: "SignedIntegral" => two's complement signed
integer data; "UnsignedIntegral" => unsigned integer data; "Real" =>
IEEE floating point data; "Index" => unsigned integer data.
</td>
</tr>
<tr>
<td>BitsPerSample</td>
<td>/Data/BitsPerSample@value: space-separated list parsed to char array.</td>
</tr>
<tr>
<td>FillOrder</td>
<td>/Data/SampleMSB@value: if all values in space-separated list are 0s =>
right-to-left; otherwise => left-to-right.
</td>
</tr>
<tr>
<td>XResolution</td>
<td>(10 / /Dimension/HorizontalPixelSize@value) or
(10 / (/Dimension/VerticalPixelSize@value *
/Dimension/PixelAspectRatio@value))</td>
</tr>
<tr>
<td>YResolution</td>
<td>(10 / /Dimension/VerticalPixelSize@value) or
(10 / (/Dimension/HorizontalPixelSize@value /
/Dimension/PixelAspectRatio@value))</td>
</tr>
<tr>
<td>ResolutionUnit</td>
<td>Centimeter if XResolution or YResolution set; otherwise None.</td>
</tr>
<tr>
<td>Orientation</td>
<td>/Dimension/ImageOrientation@value</td>
</tr>
<tr>
<td>XPosition</td>
<td>/Dimension/HorizontalPosition@value / 10</td>
</tr>
<tr>
<td>YPosition</td>
<td>/Dimension/VerticalPosition@value / 10</td>
</tr>
<tr>
<td>NewSubFileType</td>
<td>/Document/SubimageInterpretation@value: "TransparencyMask" =>
transparency mask; "ReducedResolution" => reduced-resolution;
"SinglePage" => single page.</td>
</tr>
<tr>
<td>DateTime</td>
<td>/Document/ImageCreationTime@value</td>
</tr>
<tr>
<td>DocumentName, ImageDescription, Make, Model, PageName, Software,
Artist, HostComputer, InkNames, Copyright</td>
<td>/Text/TextEntry: if /Text/TextEntry@keyword is the name of any of the
TIFF Fields, e.g., "Software", then the field is added with content
/Text/TextEntry@value and count 1.</td>
</tr>
<tr>
<td>ExtraSamples</td>
<td>/Transparency/Alpha@value: "premultiplied" => associated alpha, count 1;
"nonpremultiplied" => unassociated alpha, count 1.</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
</p>

<h4><a name="EXIFWrite"/>Writing EXIF Images</h4>

The TIFF writer may be used to write an uncompressed EXIF image or the
contents of the <tt>APP1</tt> marker segment of a compressed EXIF image.

<h5><a name="EXIFWriteTIFF"/>Writing Uncompressed EXIF Images</h5>

When writing a sequence of images each image is normally recorded as
{IFD,&nbsp;IFD Value,&nbsp;Image Data}. The EXIF specification requires
that an uncompressed EXIF image be structured as follows:

<ol>
<a name="EXIFStructure"/>
<li>Image File Header</li>
<li>Primary IFD</li>
<li>Primary IFD Value</li>
<li>Thumbnail IFD</li>
<li>Thumbnail IFD Value</li>
<li>Thumbnail Image Data</li>
<li>Primary Image Data</li>
</ol>

To meet the requirement of the primary image data being recorded last, the
primary image must be written initially as an empty image and have its data
added via pixel replacement after the thumbnail IFD and image data have been
written:

<pre><code>
ImageWriter tiffWriter;
ImageOutputStream output;
ImageWriteParam writeParam;

IIOMetadata streamMetadata;
BufferedImage image;
BufferedImage thumbnail;
IIOMetadata primaryIFD;
IIOMetadata thumbnailIFD;

tiffWriter.setOutput(output);
if(thumbnail != null) {
    // Write the TIFF header.
    tiffWriter.prepareWriteSequence(streamMetadata);

    // Append the primary IFD.
    tiffWriter.prepareInsertEmpty(-1, // append
                                  new ImageTypeSpecifier(image),
                                  image.getWidth(),
                                  image.getHeight(),
                                  primaryIFD,
                                  null, // thumbnails
                                  writeParam);
    tiffWriter.endInsertEmpty();

    // Append the thumbnail IFD and image data.
    tiffWriter.writeToSequence(new IIOImage(thumbnail, null, null),
                               writeParam);

    // Append the primary image data.
    tiffWriter.prepareReplacePixels(0, new Rectangle(image.getWidth(),
                                                     image.getHeight()));
    tiffWriter.replacePixels(image, writeParam);
    tiffWriter.endReplacePixels();

    // End writing.
    tiffWriter.endWriteSequence();
} else {
    // Write only the primary IFD and image data.
    tiffWriter.write(streamMetadata,
                     new IIOImage(image, null, primaryIFD),
                     writeParam);
}
</code></pre>

<h5><a name="EXIFWriteJPEG"/>Writing Compressed EXIF Images</h5>

<!-- XXX -->
The structure of the embedded TIFF stream in the <tt>APP1</tt> segment of a
compressed EXIF image is identical to the <a href="#EXIFStructure">
uncompressed EXIF image structure</a> except that there are no primary
image data, i.e., the primary IFD does not refer to any image data.

<pre><code>
ImageWriter tiffWriter;
ImageWriteParam writeParam;
boolean isThumbnailCompressed;

IIOMetadata streamMetadata;
BufferedImage image;
BufferedImage thumbnail;
IIOMetadata primaryIFD;
IIOMetadata thumbnailIFD;

// Set up the output.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MemoryCacheImageOutputStream app1EXIFOutput = new MemoryCacheImageOutputStream(baos);
tiffWriter.setOutput(app1EXIFOutput);

// Set compression.
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType("EXIF JPEG");

if(thumbnail != null) {
    // Write the TIFF header.
    tiffWriter.prepareWriteSequence(streamMetadata);

    // Append the primary IFD.
    tiffWriter.prepareInsertEmpty(-1,  // append
                                  new ImageTypeSpecifier(image),
                                  image.getWidth(),
                                  image.getHeight(),
                                  primaryIFD,
                                  null, // thumbnails
                                  writeParam);
    tiffWriter.endInsertEmpty();

    // Change compression type if uncompressed.
    if(!isThumbnailCompressed) {
        writeParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
    }

    // Append the thumbnail IFD and image data.
    tiffWriter.writeToSequence(new IIOImage(thumbnail, null,
                                            thumbnailIFD),
                               writeParam);

    // End writing.
    tiffWriter.endWriteSequence();
} else {
    // Write only the primary IFD.
    tiffWriter.prepareWriteEmpty(streamMetadata,
                                 new ImageTypeSpecifier(image),
                                 image.getWidth(),
                                 image.getHeight(),
                                 primaryIFD,
                                 null, // thumbnails
                                 writeParam);
    tiffWriter.endWriteEmpty();
}

// Flush data into byte stream.
app1EXIFOutput.flush();

// Create APP1 parameter array.
byte[] app1Parameters = new byte[6 + baos.size()];

// Add EXIF APP1 ID bytes.
app1Parameters[0] = (byte)'E';
app1Parameters[1] = (byte)'x';
app1Parameters[2] = (byte)'i';
app1Parameters[3] = (byte)'f';
app1Parameters[4] = app1Parameters[5] = (byte)0;

// Append TIFF stream to APP1 parameters.
System.arraycopy(baos.toByteArray(), 0, app1Parameters, 6, baos.size());

// Create the APP1 EXIF node to be added to native JPEG image metadata.
IIOMetadataNode app1Node = new IIOMetadataNode("unknown");
app1Node.setAttribute("MarkerTag", (new Integer(0xE1)).toString());
app1Node.setUserObject(app1Parameters);
</code></pre>

The <code>"unknown"</code> node created above would be appended to the
<code>"markerSequence"</code> node of the native JPEG image metadata
and written to the JPEG stream when the primary image is written using
the JPEG writer.

<h3><a name="StreamMetadata"/>Stream Metadata</h3>

The DTD for the stream metadata format is as follows:

<pre>
&lt;!DOCTYPE "com_sun_media_imageio_plugins_tiff_stream_1.0" [

  &lt;!ELEMENT "com_sun_media_imageio_plugins_tiff_stream_1.0" (ByteOrder)>

    &lt;!ELEMENT "ByteOrder" EMPTY&gt;
      &lt;!-- The stream byte order --&gt; 
      &lt;!ATTLIST "ByteOrder" "value" #CDATA #REQUIRED&gt;
        &lt;!-- One of "BIG_ENDIAN" or "LITTLE_ENDIAN" --&gt; 
        &lt;!-- Data type: String --&gt;
]&gt;
</pre>

<h3><a name="ImageMetadata"/>Image Metadata</h3>

The DTD for the native image metadata format is as follows:

<pre>
&lt;!DOCTYPE "com_sun_media_imageio_plugins_tiff_image_1.0" [

  &lt;!ELEMENT "com_sun_media_imageio_plugins_tiff_image_1.0" (TIFFIFD)*&gt;

    &lt;!ELEMENT "TIFFIFD" (TIFFField | TIFFIFD)*&gt;
      &lt;!-- An IFD (directory) containing fields --&gt; 
      &lt;!ATTLIST "TIFFIFD" "tagSets" #CDATA #REQUIRED&gt;
        &lt;!-- Data type: String --&gt;
      &lt;!ATTLIST "TIFFIFD" "parentTagNumber" #CDATA #IMPLIED&gt;
        &lt;!-- The tag number of the field pointing to this IFD --&gt; 
        &lt;!-- Data type: Integer --&gt;
      &lt;!ATTLIST "TIFFIFD" "parentTagName" #CDATA #IMPLIED&gt;
        &lt;!-- A mnemonic name for the field pointing to this IFD, if known 
             --&gt; 
        &lt;!-- Data type: String --&gt;

      &lt;!ELEMENT "TIFFField" (TIFFBytes | TIFFAsciis |
        TIFFShorts | TIFFSShorts | TIFFLongs | TIFFSLongs |
        TIFFRationals | TIFFSRationals |
        TIFFFloats | TIFFDoubles | TIFFUndefined)&gt;
        &lt;!-- A field containing data --&gt; 
        &lt;!ATTLIST "TIFFField" "number" #CDATA #REQUIRED&gt;
          &lt;!-- The tag number asociated with the field --&gt; 
          &lt;!-- Data type: String --&gt;
        &lt;!ATTLIST "TIFFField" "name" #CDATA #IMPLIED&gt;
          &lt;!-- A mnemonic name associated with the field, if known --&gt; 
          &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFBytes" (TIFFByte)*&gt;
          &lt;!-- A sequence of TIFFByte nodes --&gt; 

          &lt;!ELEMENT "TIFFByte" EMPTY&gt;
            &lt;!-- An integral value between 0 and 255 --&gt; 
            &lt;!ATTLIST "TIFFByte" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The value --&gt; 
              &lt;!-- Data type: String --&gt;
            &lt;!ATTLIST "TIFFByte" "description" #CDATA #IMPLIED&gt;
              &lt;!-- A description, if available --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFAsciis" (TIFFAscii)*&gt;
          &lt;!-- A sequence of TIFFAscii nodes --&gt; 

          &lt;!ELEMENT "TIFFAscii" EMPTY&gt;
            &lt;!-- A String value --&gt; 
            &lt;!ATTLIST "TIFFAscii" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The value --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFShorts" (TIFFShort)*&gt;
          &lt;!-- A sequence of TIFFShort nodes --&gt; 

          &lt;!ELEMENT "TIFFShort" EMPTY&gt;
            &lt;!-- An integral value between 0 and 65535 --&gt; 
            &lt;!ATTLIST "TIFFShort" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The value --&gt; 
              &lt;!-- Data type: String --&gt;
            &lt;!ATTLIST "TIFFShort" "description" #CDATA #IMPLIED&gt;
              &lt;!-- A description, if available --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFSShorts" (TIFFSShort)*&gt;
          &lt;!-- A sequence of TIFFSShort nodes --&gt; 

          &lt;!ELEMENT "TIFFSShort" EMPTY&gt;
            &lt;!-- An integral value between -32768 and 32767 --&gt; 
            &lt;!ATTLIST "TIFFSShort" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The value --&gt; 
              &lt;!-- Data type: String --&gt;
            &lt;!ATTLIST "TIFFSShort" "description" #CDATA #IMPLIED&gt;
              &lt;!-- A description, if available --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFLongs" (TIFFLong)*&gt;
          &lt;!-- A sequence of TIFFLong nodes --&gt; 

          &lt;!ELEMENT "TIFFLong" EMPTY&gt;
            &lt;!-- An integral value between 0 and 4294967295 --&gt; 
            &lt;!ATTLIST "TIFFLong" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The value --&gt; 
              &lt;!-- Data type: String --&gt;
            &lt;!ATTLIST "TIFFLong" "description" #CDATA #IMPLIED&gt;
              &lt;!-- A description, if available --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFSLongs" (TIFFSLong)*&gt;
          &lt;!-- A sequence of TIFFSLong nodes --&gt; 

          &lt;!ELEMENT "TIFFSLong" EMPTY&gt;
            &lt;!-- An integral value between -2147483648 and 2147482647 --&gt; 
            &lt;!ATTLIST "TIFFSLong" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The value --&gt; 
              &lt;!-- Data type: String --&gt;
            &lt;!ATTLIST "TIFFSLong" "description" #CDATA #IMPLIED&gt;
              &lt;!-- A description, if available --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFRationals" (TIFFRational)*&gt;
          &lt;!-- A sequence of TIFFRational nodes --&gt; 

          &lt;!ELEMENT "TIFFRational" EMPTY&gt;
            &lt;!-- A rational value consisting of an unsigned numerator and 
                 denominator --&gt; 
            &lt;!ATTLIST "TIFFRational" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The numerator and denominator, separated by a slash --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFSRationals" (TIFFSRational)*&gt;
          &lt;!-- A sequence of TIFFSRational nodes --&gt; 

          &lt;!ELEMENT "TIFFSRational" EMPTY&gt;
            &lt;!-- A rational value consisting of a signed numerator and 
                 denominator --&gt; 
            &lt;!ATTLIST "TIFFSRational" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The numerator and denominator, separated by a slash --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFFloats" (TIFFFloat)*&gt;
          &lt;!-- A sequence of TIFFFloat nodes --&gt; 

          &lt;!ELEMENT "TIFFFloat" EMPTY&gt;
            &lt;!-- A single-precision floating-point value --&gt; 
            &lt;!ATTLIST "TIFFFloat" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The value --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFDoubles" (TIFFDouble)*&gt;
          &lt;!-- A sequence of TIFFDouble nodes --&gt; 

          &lt;!ELEMENT "TIFFDouble" EMPTY&gt;
            &lt;!-- A double-precision floating-point value --&gt; 
            &lt;!ATTLIST "TIFFDouble" "value" #CDATA #IMPLIED&gt;
              &lt;!-- The value --&gt; 
              &lt;!-- Data type: String --&gt;

        &lt;!ELEMENT "TIFFUndefined" EMPTY&gt;
          &lt;!-- Uninterpreted byte data --&gt; 
          &lt;!ATTLIST "TIFFUndefined" "value" #CDATA #IMPLIED&gt;
            &lt;!-- A list of comma-separated byte values --&gt; 
            &lt;!-- Data type: String --&gt;
]&gt;
</pre>

@since 1.0

</body>
</html>
